﻿/************************************************************************
 * CoreDES - A small and core library for discrete event systems
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     CoreDES\Utils\PseudoRNG.cs
 * Description  :     A set of Pseudo Random Generation
 * Change log   :     -2010: initial version (originaly belongs to SimGine Library)
 * To Do        :
 ************************************************************************/
using System;
using System.Collections.Generic;

namespace CoreDES.Utils
{
    /// <summary>
    /// Interface for Pseudo Random Number Generators
    /// It is used to generate a pseudo random number (distribitued uniformly in [0,1])
    /// </summary>
    public interface IPseudoRNG
    {
        void init();
        void init(int seed);
        double NextDouble();
    }


    /// <summary>
    /// Default Random Number Generator which exists in Microsoft .Net Framework
    /// </summary>
    public class DotNetRNG : IPseudoRNG
    {
        Random rnd;
        public DotNetRNG()
        {
            init();
        }

        public void init()
        {
            rnd = new Random();
        }

        public void init(int seed)
        {
            rnd = new Random(seed);
        }

        public double NextDouble()
        {
            return rnd.NextDouble();
        }
    }

    /// <summary>
    /// Lagged Fibonacci generator (LFG)
    /// Based on: http://en.wikipedia.org/wiki/Lagged_Fibonacci_generator
    /// </summary>
    public class LFG : IPseudoRNG
    {

        #region IPseudoRNG Members

        public void init()
        {
            throw new NotImplementedException();
        }

        public void init(int seed)
        {
            throw new NotImplementedException();
        }

        public double NextDouble()
        {
            throw new NotImplementedException();
        }
        #endregion
    }


    /// <summary>
    /// Tausworth RNG
    /// Code from: http://blog.wonderwhy-er.com/UnityTests/Tausworth.cs
    /// </summary>
    public class Tausworth : IPseudoRNG
    {
        uint s1 = 390451501, s2 = 613566701, s3 = 858993401;
        uint mask1, mask2, mask3;
        int shft1, shft2, shft3;
        int k1 = 31, k2 = 29, k3 = 28, q1 = 13, q2 = 2, q3 = 3, p1 = 12, p2 = 4, p3 = 17;

        public void init()
        {
            setUIntSeed(0, 0, 0);
        }

        public void init(int seed)
        {
            setUIntSeed((uint)seed, (uint)seed, (uint)seed);
        }

        public Tausworth(uint a, uint b, uint c)
        {
            setUIntSeed(a, b, c);
            nextUINT();
        }

        public void setUIntSeed(uint a, uint b, uint c)
        {
            shft1 = k1 - p1;
            shft2 = k2 - p2;
            shft3 = k3 - p3;
            mask1 = UInt32.MaxValue << (32 - k1);
            mask2 = UInt32.MaxValue << (32 - k2);
            mask3 = UInt32.MaxValue << (32 - k3);
            if (a > (1 << (32 - k1)))
            {
                s1 = a;
            }
            if (b > (1 << (32 - k2)))
            {
                s2 = b;
            }
            if (c > (1 << (32 - k3)))
            {
                s3 = c;
            }
            nextUINT();
        }

        public uint nextUINT()
        {
            uint b;

            b = ((s1 << q1) ^ s1) >> shft1;
            s1 = ((s1 & mask1) << p1) ^ b;
            b = ((s2 << q2) ^ s2) >> shft2;
            s2 = ((s2 & mask2) << p2) ^ b;
            b = ((s3 << q3) ^ s3) >> shft3;
            s3 = ((s3 & mask3) << p3) ^ b;
            return (s1 ^ s2 ^ s3);
        }

        public double NextDouble()
        {
            return nextUINT() / (double)UInt32.MaxValue;
        }
    }
}
